﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kokomo.Mirror.CliMetadata.Tables;

namespace Kokomo.Mirror.CliMetadata
{
	class CliGenericParamType : CliTypeBase
	{
		internal CliGenericParamType(
			GenericParamRow genericParamRow,
			CliMemberBasicInfo basicInfo,
			CliMethodInfo declaringMethod
			)
			: base(basicInfo)
		{
			this._genericParamRow = genericParamRow;
			this._declaringMethod = declaringMethod;

			this._baseType = new Lazy<Type>(this.LoadBaseType);
		}

		#region Generic
		public override System.Reflection.GenericParameterAttributes GenericParameterAttributes
		{
			get { return this._genericParamRow.Flags; }
		}
		public override int GenericParameterPosition
		{
			get { return this._genericParamRow.Number; }
		}
		public override Type[] GetGenericParameterConstraints()
		{
			return this.basicInfo.module.QueryGenericParamConstraints(
				this.MetadataToken,
				((object)this.DeclaringType != null) ? this.DeclaringType.GetGenericArguments() : null,
				((object)this.DeclaringMethod != null) ? this.DeclaringMethod.GetGenericArguments() : null
				);
		}
		#endregion

		#region Info
		private GenericParamRow _genericParamRow;

		public override string Name
		{
			get { return this.basicInfo.module.Metadata.GetString(this._genericParamRow.Name); }
		}

		public override bool IsGenericParameter
		{
			get { return true; }
		}

		private Lazy<Type> _baseType;
		private Type LoadBaseType()
		{
			Type[] genericTypeArguments = null;
			Type[] genericMethodArguments = null;
			if ((object)this.DeclaringType != null)
				genericTypeArguments = this.DeclaringType.GetGenericArguments();
			if ((object)this.DeclaringMethod != null)
				genericMethodArguments = this.DeclaringMethod.GetGenericArguments();

			Type[] constraints = this.basicInfo.module.QueryGenericParamConstraints(
				this.MetadataToken,
				genericTypeArguments,
				genericMethodArguments
				);
			Type baseType = constraints.FirstOrDefault();
			if ((object)baseType == null)
			{
				if (this.GenericParameterAttributes.HasFlag(System.Reflection.GenericParameterAttributes.NotNullableValueTypeConstraint))
					baseType = this.basicInfo.module.CliAssembly.LoadContext.ValueType;
				else
					baseType = this.basicInfo.module.CliAssembly.LoadContext.GetType(TypeCode.Object);
			}

			return baseType;
		}
		public override Type BaseType
		{
			get { return this._baseType.Value; }
		}

		private CliMethodInfo _declaringMethod;
		public override System.Reflection.MethodBase DeclaringMethod
		{
			get
			{
				return this._declaringMethod;
			}
		}
		#endregion

		protected override System.Reflection.TypeAttributes GetAttributeFlagsImpl()
		{
			return System.Reflection.TypeAttributes.AnsiClass | System.Reflection.TypeAttributes.Public;
		}

		public override string Namespace
		{
			get { return null; }
		}

		public override Type Reflect()
		{
			Type declaring = this.DeclaringType.Reflect();
			return declaring.GetGenericArguments()[this.GenericParameterPosition];
		}
	}
}
